"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const vue_1 = require("vue");
const treemate_1 = require("treemate");
const vueuc_1 = require("vueuc");
const seemly_1 = require("seemly");
const empty_1 = require("../../../empty");
const scrollbar_1 = require("../../scrollbar");
const _utils_1 = require("../../../_utils");
const cssr_1 = require("../../../_utils/cssr");
const _mixins_1 = require("../../../_mixins");
const loading_1 = __importDefault(require("../../loading"));
const focus_detector_1 = __importDefault(require("../../focus-detector"));
const SelectOption_1 = __importDefault(require("./SelectOption"));
const SelectGroupHeader_1 = __importDefault(require("./SelectGroupHeader"));
const index_cssr_1 = __importDefault(require("./styles/index.cssr"));
const styles_1 = require("../styles");
const interface_1 = require("./interface");
exports.default = (0, vue_1.defineComponent)({
    name: 'InternalSelectMenu',
    props: Object.assign(Object.assign({}, _mixins_1.useTheme.props), { clsPrefix: {
            type: String,
            required: true
        }, scrollable: {
            type: Boolean,
            default: true
        }, treeMate: {
            type: Object,
            required: true
        }, multiple: Boolean, size: {
            type: String,
            default: 'medium'
        }, value: {
            type: [String, Number, Array],
            default: null
        }, width: [Number, String], autoPending: Boolean, virtualScroll: {
            type: Boolean,
            default: true
        }, 
        // show is used to toggle pending state initialization
        show: {
            type: Boolean,
            default: true
        }, loading: Boolean, focusable: Boolean, renderLabel: Function, renderOption: Function, onMousedown: Function, onScroll: Function, onFocus: Function, onBlur: Function, onKeyup: Function, onKeydown: Function, onTabOut: Function, onMouseenter: Function, onMouseleave: Function, resetMenuOnOptionsChange: {
            type: Boolean,
            default: true
        }, 
        // deprecated
        onToggle: Function }),
    setup(props) {
        const themeRef = (0, _mixins_1.useTheme)('InternalSelectMenu', 'InternalSelectMenu', index_cssr_1.default, styles_1.internalSelectMenuLight, props, (0, vue_1.toRef)(props, 'clsPrefix'));
        const selfRef = (0, vue_1.ref)(null);
        const virtualListRef = (0, vue_1.ref)(null);
        const scrollbarRef = (0, vue_1.ref)(null);
        const flattenedNodesRef = (0, vue_1.computed)(() => props.treeMate.getFlattenedNodes());
        const fIndexGetterRef = (0, vue_1.computed)(() => (0, treemate_1.createIndexGetter)(flattenedNodesRef.value));
        const pendingNodeRef = (0, vue_1.ref)(null);
        function initPendingNode() {
            const { treeMate } = props;
            setPendingTmNode(props.autoPending
                ? props.value === null
                    ? treeMate.getFirstAvailableNode()
                    : props.multiple
                        ? treeMate.getNode((props.value || [])[(props.value || [])
                            .length - 1]) || treeMate.getFirstAvailableNode()
                        : treeMate.getNode(props.value) ||
                            treeMate.getFirstAvailableNode()
                : null);
        }
        // initPendingNode()
        // onMounted(() => {
        //   watchEffect(() => {
        //     if (props.show) {
        //       initPendingNode()
        //       void nextTick(scrollToPendingNode)
        //     }
        //   })
        // })
        function clearPendingNodeIfInvalid() {
            const { value: pendingNode } = pendingNodeRef;
            if (pendingNode && !props.treeMate.getNode(pendingNode.key)) {
                pendingNodeRef.value = null;
            }
        }
        let initPendingNodeWatchStopHandle;
        (0, vue_1.watch)(() => props.show, (show) => {
            if (show) {
                initPendingNodeWatchStopHandle = (0, vue_1.watch)(() => props.treeMate, () => {
                    if (props.resetMenuOnOptionsChange) {
                        if (props.autoPending) {
                            initPendingNode();
                        }
                        else {
                            clearPendingNodeIfInvalid();
                        }
                        void (0, vue_1.nextTick)(scrollToPendingNode);
                    }
                    else {
                        clearPendingNodeIfInvalid();
                    }
                }, {
                    immediate: true
                });
            }
            else {
                initPendingNodeWatchStopHandle === null || initPendingNodeWatchStopHandle === void 0 ? void 0 : initPendingNodeWatchStopHandle();
            }
        }, {
            immediate: true
        });
        (0, vue_1.onBeforeUnmount)(() => {
            initPendingNodeWatchStopHandle === null || initPendingNodeWatchStopHandle === void 0 ? void 0 : initPendingNodeWatchStopHandle();
        });
        const itemSizeRef = (0, vue_1.computed)(() => {
            return (0, seemly_1.depx)(themeRef.value.self[(0, cssr_1.createKey)('optionHeight', props.size)]);
        });
        const paddingRef = (0, vue_1.computed)(() => {
            return (0, seemly_1.getPadding)(themeRef.value.self[(0, cssr_1.createKey)('padding', props.size)]);
        });
        const valueSetRef = (0, vue_1.computed)(() => {
            if (props.multiple && Array.isArray(props.value)) {
                return new Set(props.value);
            }
            return new Set();
        });
        const emptyRef = (0, vue_1.computed)(() => {
            const tmNodes = flattenedNodesRef.value;
            return tmNodes && tmNodes.length === 0;
        });
        const styleRef = (0, vue_1.computed)(() => {
            return [{ width: (0, _utils_1.formatLength)(props.width) }, cssVarsRef.value];
        });
        (0, vue_1.watch)((0, vue_1.toRef)(props, 'treeMate'), () => {
            if (props.autoPending) {
                const tmNode = props.treeMate.getFirstAvailableNode();
                setPendingTmNode(tmNode);
            }
            else {
                setPendingTmNode(null);
            }
        });
        function doToggle(tmNode) {
            const { onToggle } = props;
            if (onToggle)
                onToggle(tmNode);
        }
        function doScroll(e) {
            const { onScroll } = props;
            if (onScroll)
                onScroll(e);
        }
        // required, scroller sync need to be triggered manually
        function handleVirtualListScroll(e) {
            var _a;
            (_a = scrollbarRef.value) === null || _a === void 0 ? void 0 : _a.sync();
            doScroll(e);
        }
        function handleVirtualListResize() {
            var _a;
            (_a = scrollbarRef.value) === null || _a === void 0 ? void 0 : _a.sync();
        }
        function getPendingTmNode() {
            const { value: pendingTmNode } = pendingNodeRef;
            if (pendingTmNode)
                return pendingTmNode;
            return null;
        }
        function handleOptionMouseEnter(e, tmNode) {
            if (tmNode.disabled)
                return;
            setPendingTmNode(tmNode, false);
        }
        function handleOptionClick(e, tmNode) {
            if (tmNode.disabled)
                return;
            doToggle(tmNode);
        }
        // keyboard related methods
        function handleKeyUp(e) {
            var _a;
            if ((0, seemly_1.happensIn)(e, 'action'))
                return;
            (_a = props.onKeyup) === null || _a === void 0 ? void 0 : _a.call(props, e);
        }
        function handleKeyDown(e) {
            var _a;
            if ((0, seemly_1.happensIn)(e, 'action'))
                return;
            (_a = props.onKeydown) === null || _a === void 0 ? void 0 : _a.call(props, e);
        }
        function handleMouseDown(e) {
            var _a;
            (_a = props.onMousedown) === null || _a === void 0 ? void 0 : _a.call(props, e);
            if (props.focusable)
                return;
            e.preventDefault();
        }
        function next() {
            const { value: pendingTmNode } = pendingNodeRef;
            if (pendingTmNode) {
                setPendingTmNode(pendingTmNode.getNext({ loop: true }), true);
            }
        }
        function prev() {
            const { value: pendingTmNode } = pendingNodeRef;
            if (pendingTmNode) {
                setPendingTmNode(pendingTmNode.getPrev({ loop: true }), true);
            }
        }
        function setPendingTmNode(tmNode, doScroll = false) {
            pendingNodeRef.value = tmNode;
            if (doScroll)
                scrollToPendingNode();
        }
        function scrollToPendingNode() {
            var _a, _b;
            const tmNode = pendingNodeRef.value;
            if (!tmNode)
                return;
            const fIndex = fIndexGetterRef.value(tmNode.key);
            if (fIndex === null)
                return;
            if (props.virtualScroll) {
                (_a = virtualListRef.value) === null || _a === void 0 ? void 0 : _a.scrollTo({ index: fIndex });
            }
            else {
                (_b = scrollbarRef.value) === null || _b === void 0 ? void 0 : _b.scrollTo({
                    index: fIndex,
                    elSize: itemSizeRef.value
                });
            }
        }
        function handleFocusin(e) {
            var _a, _b;
            if ((_a = selfRef.value) === null || _a === void 0 ? void 0 : _a.contains(e.target)) {
                (_b = props.onFocus) === null || _b === void 0 ? void 0 : _b.call(props, e);
            }
        }
        function handleFocusout(e) {
            var _a, _b;
            if (!((_a = selfRef.value) === null || _a === void 0 ? void 0 : _a.contains(e.relatedTarget))) {
                (_b = props.onBlur) === null || _b === void 0 ? void 0 : _b.call(props, e);
            }
        }
        (0, vue_1.provide)(interface_1.internalSelectionMenuInjectionKey, {
            handleOptionMouseEnter,
            handleOptionClick,
            valueSetRef,
            multipleRef: (0, vue_1.toRef)(props, 'multiple'),
            valueRef: (0, vue_1.toRef)(props, 'value'),
            renderLabelRef: (0, vue_1.toRef)(props, 'renderLabel'),
            renderOptionRef: (0, vue_1.toRef)(props, 'renderOption'),
            pendingTmNodeRef: pendingNodeRef
        });
        (0, vue_1.provide)(interface_1.internalSelectionMenuBodyInjectionKey, selfRef);
        (0, vue_1.onMounted)(() => {
            const { value } = scrollbarRef;
            if (value)
                value.sync();
        });
        const cssVarsRef = (0, vue_1.computed)(() => {
            const { size } = props;
            const { common: { cubicBezierEaseInOut }, self: { height, borderRadius, color, groupHeaderTextColor, actionDividerColor, optionTextColorPressed, optionTextColor, optionTextColorDisabled, optionTextColorActive, optionOpacityDisabled, optionCheckColor, actionTextColor, optionColorPending, optionColorActive, loadingColor, loadingSize, [(0, cssr_1.createKey)('optionFontSize', size)]: fontSize, [(0, cssr_1.createKey)('optionHeight', size)]: optionHeight, [(0, cssr_1.createKey)('optionPadding', size)]: optionPadding } } = themeRef.value;
            return {
                '--height': height,
                '--action-divider-color': actionDividerColor,
                '--action-text-color': actionTextColor,
                '--bezier': cubicBezierEaseInOut,
                '--border-radius': borderRadius,
                '--color': color,
                '--option-font-size': fontSize,
                '--group-header-text-color': groupHeaderTextColor,
                '--option-check-color': optionCheckColor,
                '--option-color-pending': optionColorPending,
                '--option-color-active': optionColorActive,
                '--option-height': optionHeight,
                '--option-opacity-disabled': optionOpacityDisabled,
                '--option-text-color': optionTextColor,
                '--option-text-color-active': optionTextColorActive,
                '--option-text-color-disabled': optionTextColorDisabled,
                '--option-text-color-pressed': optionTextColorPressed,
                '--option-padding': optionPadding,
                '--option-padding-left': (0, seemly_1.getPadding)(optionPadding, 'left'),
                '--loading-color': loadingColor,
                '--loading-size': loadingSize
            };
        });
        const exposedProps = {
            selfRef,
            next,
            prev,
            getPendingTmNode
        };
        return Object.assign({ mergedTheme: themeRef, virtualListRef,
            scrollbarRef, style: styleRef, itemSize: itemSizeRef, padding: paddingRef, flattenedNodes: flattenedNodesRef, empty: emptyRef, virtualListContainer() {
                const { value } = virtualListRef;
                return value === null || value === void 0 ? void 0 : value.listElRef;
            },
            virtualListContent() {
                const { value } = virtualListRef;
                return value === null || value === void 0 ? void 0 : value.itemsElRef;
            },
            doScroll,
            handleFocusin,
            handleFocusout,
            handleKeyUp,
            handleKeyDown,
            handleMouseDown,
            handleVirtualListResize,
            handleVirtualListScroll }, exposedProps);
    },
    render() {
        const { $slots, virtualScroll, clsPrefix, mergedTheme } = this;
        return ((0, vue_1.h)("div", { ref: "selfRef", tabindex: this.focusable ? 0 : -1, class: [
                `${clsPrefix}-base-select-menu`,
                this.multiple && `${clsPrefix}-base-select-menu--multiple`
            ], style: this.style, onFocusin: this.handleFocusin, onFocusout: this.handleFocusout, onKeyup: this.handleKeyUp, onKeydown: this.handleKeyDown, onMousedown: this.handleMouseDown, onMouseenter: this.onMouseenter, onMouseleave: this.onMouseleave },
            this.loading ? ((0, vue_1.h)("div", { class: `${clsPrefix}-base-select-menu__loading` },
                (0, vue_1.h)(loading_1.default, { clsPrefix: clsPrefix, strokeWidth: 20 }))) : !this.empty ? ((0, vue_1.h)(scrollbar_1.NScrollbar, { ref: "scrollbarRef", theme: mergedTheme.peers.Scrollbar, themeOverrides: mergedTheme.peerOverrides.Scrollbar, scrollable: this.scrollable, container: virtualScroll ? this.virtualListContainer : undefined, content: virtualScroll ? this.virtualListContent : undefined, onScroll: virtualScroll ? undefined : this.doScroll }, {
                default: () => {
                    return virtualScroll ? ((0, vue_1.h)(vueuc_1.VirtualList, { ref: "virtualListRef", class: `${clsPrefix}-virtual-list`, items: this.flattenedNodes, itemSize: this.itemSize, showScrollbar: false, paddingTop: this.padding.top, paddingBottom: this.padding.bottom, onResize: this.handleVirtualListResize, onScroll: this.handleVirtualListScroll, itemResizable: true }, {
                        default: ({ item: tmNode }) => {
                            return tmNode.isGroup ? ((0, vue_1.h)(SelectGroupHeader_1.default, { key: tmNode.key, clsPrefix: clsPrefix, tmNode: tmNode })) : tmNode.ignored ? null : ((0, vue_1.h)(SelectOption_1.default, { clsPrefix: clsPrefix, key: tmNode.key, tmNode: tmNode }));
                        }
                    })) : ((0, vue_1.h)("div", { class: `${clsPrefix}-base-select-menu-option-wrapper`, style: {
                            paddingTop: this.padding.top,
                            paddingBottom: this.padding.bottom
                        } }, this.flattenedNodes.map((tmNode) => tmNode.isGroup ? ((0, vue_1.h)(SelectGroupHeader_1.default, { key: tmNode.key, clsPrefix: clsPrefix, tmNode: tmNode })) : ((0, vue_1.h)(SelectOption_1.default, { clsPrefix: clsPrefix, key: tmNode.key, tmNode: tmNode })))));
                }
            })) : ((0, vue_1.h)("div", { class: `${clsPrefix}-base-select-menu__empty` }, (0, vue_1.renderSlot)($slots, 'empty', undefined, () => [
                (0, vue_1.h)(empty_1.NEmpty, { theme: mergedTheme.peers.Empty, themeOverrides: mergedTheme.peerOverrides.Empty })
            ]))),
            $slots.action && ((0, vue_1.h)("div", { class: `${clsPrefix}-base-select-menu__action`, "data-action": true }, (0, vue_1.renderSlot)($slots, 'action'))),
            $slots.action && (0, vue_1.h)(focus_detector_1.default, { onFocus: this.onTabOut })));
    }
});
